package com.yuyou.fn.tms.util;


import com.yuyou.fn.common.util.Utils;
import com.yuyou.fn.tms.exception.SignException;
import org.apache.commons.lang3.StringUtils;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class Signs {


    //生成随机数字和字母,
    public static String getStringRandom(int length) {

        String val = "";
        Random random = new Random();

        //参数length，表示生成几位随机数
        for (int i = 0; i < length; i++) {

            String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";
            //输出字母还是数字
            if ("char".equalsIgnoreCase(charOrNum)) {
                //输出是大写字母还是小写字母
                //int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;
                int temp = 65;
                val += (char) (random.nextInt(26) + temp);
            } else if ("num".equalsIgnoreCase(charOrNum)) {
                val += String.valueOf(random.nextInt(10));
            }
        }
        return val;
    }

    //按照业务规则重新组合加密串
    public static String getNewKey(String key, String random) {
        String val = null;
        if (key == null || "".equals(key) || key.length() != 32) {
            return null;
        }
        if (random == null || "".equals(random) || random.length() != 16) {
            return null;
        }
        val = key.substring(0, 3) + random.substring(0, 1) + random.substring(1, 2) + key.substring(3, 7)
                + random.substring(2, 3) + key.substring(7, 8) + random.substring(3, 4) + key.substring(8, 19)
                + random.substring(4, 5) + random.substring(5, 6) + random.substring(6, 7) + key.substring(19, 21) + random.substring(7, 8)
                + key.substring(21, 22) + random.substring(8, 9) + random.substring(9, 10) + key.substring(22, 24) + random.substring(10, 11)
                + key.substring(24, 27) + random.substring(11, 12) + key.substring(27, 29)
                + random.substring(12, 13) + key.substring(29, 30) + random.substring(13, 14)
                + key.substring(30, 31) + random.substring(14, 15) + key.substring(31, 32) + random.substring(15, 16);
        return val;
    }

    //加密 并且自动将英文转换成大写
    public static String getMD5String(String str) {
        try {
            byte[] res = str.getBytes();
            MessageDigest md = MessageDigest.getInstance("MD5".toUpperCase());
            for (int i = 0; i < res.length; i++) {
                md.update(res[i]);
            }
            byte[] hash = md.digest();

            StringBuffer d = new StringBuffer("");
            for (int i = 0; i < hash.length; i++) {
                int v = hash[i] & 0xFF;
                if (v < 16) d.append("0");
                d.append(Integer.toString(v, 16).toUpperCase() + "");
            }
            return d.toString();
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 校验密码
     *
     * @param keyString 密码串
     * @param timeNo    时间戳
     * @return
     */
    public static void validateKey(String keyString, String timeNo, String key) {
        if (keyString != null && keyString.equals(key)) {
            return;
        }
        if (timeNo == null || "".equals(timeNo)) {
            throw new SignException("传递的加密字符有错");
        }
        Long t = (System.currentTimeMillis() - Long.valueOf(timeNo)) / (60 * 1000);
        if (t > 5 || t < 0) {
            throw new SignException("传递的加密字符已失效");
        }
        Map map = parseNewKey(keyString);
        if (map == null) {
            throw new SignException("传递的加密字符有错");
        }
        String initKey = (String) map.get("initKey");
        String random = (String) map.get("random");
        String md5Key = getMD5String(key + timeNo + random);
        if (!initKey.equals(md5Key)) {
            throw new SignException("传递的加密字符有错");
        }
    }

    //按照业务规则分解加密串
    public static Map parseNewKey(String key) {
        Map map = new HashMap();
        String val = null;
        String random = null;
        if (key == null || "".equals(key) || key.length() != 48) {
            return null;
        }
        //640%% F4F6% 6%BD2 B7718 590%% %5F%6 %%42% 1CB%D 3%2%4 %D%
        val = key.substring(0, 3) + key.substring(5, 9) + key.substring(10, 11) + key.substring(12, 23)
                + key.substring(26, 28) + key.substring(29, 30) + key.substring(32, 34) + key.substring(35, 38)
                + key.substring(39, 41) + key.substring(42, 43) + key.substring(44, 45) + key.substring(46, 47);

        random = key.substring(3, 5) + key.substring(9, 10) + key.substring(11, 12) + key.substring(23, 26)
                + key.substring(28, 29) + key.substring(30, 32) + key.substring(34, 35) + key.substring(38, 39)
                + key.substring(41, 42) + key.substring(43, 44) + key.substring(45, 46) + key.substring(47, 48);
        map.put("initKey", val);
        map.put("random", random);
        return map;
    }

    /***
     * 请求参数额外增加时间戳（timeStamp）和校验码(sign)，生成的校验码(sign)规则：
     1.使用md5(appKey+必填参数+时间戳+盐值），生成后的32位大写的md5需要跟随机生成的10位盐值（salt）按一定规则（前5位salt+md5+后5位salt）进行混淆后字符串作为最终的校验码，总长度42位。
     2.被调用端对校验码按规则解析出盐值（salt）和md5值后，按规则：md5(appKey+必填参数+时间戳+盐值）生成的md5值与解析出来的md5进行比较，必填参数按参数名自然排序。
     3.为了防止截取到相同的请求参数后进行重放攻击，需要对时间戳进行再次校验，只有在允许的时间范围内才可以调用，被调用端当前时间不能超过时间戳1分钟。
     *
     * @param appkey
     * @return
     * @throws Exception
     */
    public static void validResourceSign(String timeStamp, TreeMap<String,String> map,String sign, Date lastModifyTime, String appkey){
        if (sign != null && sign.equals(appkey)) {
            return;
        }
        Long current = System.currentTimeMillis();
        if (current - Long.parseLong(timeStamp) >= 6000000) {
            throw new SignException("签名过期");
        }

        Map<String, String> md5Params = new LinkedHashMap<String, String>();
        StringBuilder sb = new StringBuilder();

        String ts = timeStamp;


        String salt = sign.substring(0, 5) + sign.substring(sign.length() - 5, sign.length());

        sign = sign.substring(5, sign.length() - 5);
        md5Params.put("appkey", appkey);//将分配的appkey加入排序
        if (map!=null){
            md5Params.putAll(map);
        }
        if (lastModifyTime!=null){
            md5Params.put("lastModifyTime", Utils.formatDate(lastModifyTime, "yyyy-MM-dd HH:mm:ss"));
        }
        md5Params.put("timeStamp", ts);
        md5Params.put("salt", salt);
        for (String key : md5Params.keySet()) {
            String value = md5Params.get(key);
            if (StringUtils.isNotEmpty(value))
                sb.append(key).append("=").append(value).append("&");
        }
        if (sb.length() > 0) {
            sb.deleteCharAt(sb.length() - 1);
        }
        String blank = sb.toString();
        try {
            String genMd5 = md5(blank.getBytes("utf-8"));
            if (!sign.toLowerCase().equals(genMd5)) {
                throw new SignException("签名错误");
            }
        }catch (Exception e){
            throw new SignException("签名错误");
        }




    }

    public static String md5(byte[] b) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.reset();
            md.update(b);
            byte[] hash = md.digest();
            StringBuffer outStrBuf = new StringBuffer(32);
            for (int i = 0; i < hash.length; i++) {
                int v = hash[i] & 0xFF;
                if (v < 16) {
                    outStrBuf.append('0');
                }
                outStrBuf.append(Integer.toString(v, 16).toLowerCase());
            }
            return outStrBuf.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return new String(b);
        }

    }




}
